const {chromium} = require('playwright');
const fs = require('fs');
const path = require('path');
const dayjs = require('dayjs');

async function crawlWebsite(option) {
  // 默认是无头模式, 自己调试时可以改成false
  const browser = await chromium.launch({headless: true});
  let page = await browser.newPage();
  let results = [];
  let crawledPages = 0;

  // 辅助函数，用于执行带有重试机制的页面访问
  async function tryGoto(url, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
      try {
        await page.goto(url, {waitUntil: 'domcontentloaded'});
        await page.waitForTimeout(3000); // 等待额外的时间以确保页面完全加载
        break; // 如果成功，则退出循环
      } catch (error) {
        console.warn(`重试访问页面：${url}，剩余尝试次数：${maxRetries - i - 1}`);
        if (i === maxRetries - 1) throw error; // 如果已达到最大重试次数，则抛出错误
        await page.close(); // 关闭当前页面
        page = await browser.newPage(); // 打开一个新的页面
      }
    }
  }

  // 收集初始页面的链接
  await tryGoto(option.url);
  let links = await page.$$eval('a', (anchors, match) =>
      anchors.map(a => a.href).filter(href => new RegExp(match.replace(/\*/g, '.*')).test(href)),
    option.match
  );

// 将初始 URL 添加到链接列表中，并去重
  links = Array.from(new Set([option.url, ...links]));
  console.log(`共找到 ${links.length} 个链接, links内容为:`, links);
  console.log(`最多只爬取${links.length > option.maxPagesToCrawl ? option.maxPagesToCrawl : links.length}个页面`);
  // 根据 option.maxPagesToCrawl 截取数组长度
  links = links.slice(0, option.maxPagesToCrawl);

  // 爬取函数
  async function crawl(url) {
    try {
      await tryGoto(url); // 使用重试机制访问页面

      const title = await page.title();
      const content = await page.innerText(option.selector || 'body', {timeout: 10000});
      results.push({title, url, html: content});

      crawledPages++;
    } catch (e) {
      console.warn(`无法访问或提取内容：${url}`);
    }
  }

  // 遍历所有链接并爬取
  for (let i = 0; i < links.length; i++) {
    const link = links[i];
    console.log(`正在爬取：${link}`, i, links.length);
    await crawl(link);
  }

  await browser.close();

  console.log(`爬取完成。共爬取页面数：${crawledPages}`);
  return results;
}


// 从项目根目录读取配置文件
const option = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'option.json'), 'utf8'));

crawlWebsite(option).then(results => {
  const dateStr = dayjs().format('YYYYMMDDHHmmss');
  const outputFileName = `output-${dateStr}.txt`; // 构造文件名
  const outputPath = path.join(__dirname, '..', 'target', outputFileName);

  // 检查 target 目录是否存在，如果不存在则创建它
  const targetDir = path.dirname(outputPath);
  if (!fs.existsSync(targetDir)) {
    fs.mkdirSync(targetDir, {recursive: true});
  }

  console.log(`正在保存结果到：${outputPath}`);
  fs.writeFileSync(outputPath, JSON.stringify(results, null, 2));
  console.log('结果保存完毕。');
});



